using Vin.Extension.Framework.Base;
using Vin.Extension.Framework.Dto;
using Vin.Extension.Framework.Monitor.Constants;
using Vin.Extension.Framework.Monitor.Dto.Entities;
using Vin.Extension.Framework.Monitor.Options;
using Vin.Extension.Framework.Monitor.ServiceInterface;
using Vin.Extension.JwtBearer.Extension;
using Vin.Extension.JwtBearer.Models;
using Vin.Extension.JwtBearer.Tool;
using Vin.Tool.AspNetCore.Singleton;
using Vin.Tool.Core.BasicTypeCategory;
using Vin.Tool.Core.NetCategory;

namespace Vin.Extension.Framework.Monitor.Service;

public class SysOnlineService : VinFrameworkBaseService, ISysOnlineService
{
    public async Task<List<SysOnlineDto>> ListAsync(VinFrameworkBaseListDto dto)
    {
        var list = new List<SysOnlineDto>();

        long cursor = 0;
        do
        {
            var result = await RedisCache.ScanAsync(cursor,
                MonitorConstants.OnlineKey.Format("*"),
                100);
            cursor = result.cursor;

            foreach (var key in result.items)
            {
                var userId = key.Split(":").LastOrDefault();
                var lastTime = await RedisCache.GetCacheAsync<DateTime>(key);
                var user = await RedisCache.GetCacheAsync<VinJwtTokenModel>(VinSecurityTool.TokenKey(dto.Keyword) +
                                                                            userId);
                if (user != null)
                {
                    list.Add(new SysOnlineDto
                    {
                        SessionId = userId,
                        AccountName = user.AccountName,
                        UserName = user.UserName,
                        Ip = user.Ip,
                        Browser = user.Browser,
                        Location = user.Location,
                        Os = user.Os,
                        LoginTime = user.LoginTime,
                        LastHeartbeatTime = lastTime
                    });
                }
            }
        } while (cursor > 0);

        return list;
    }

    public async Task<bool> HeartbeatAsync()
    {
        var option = VinApp.GetConfig<MonitorOption>();
        var tokenKey = VinSecurityTool.TokenKey() + HttpContext.GetUserId()!;
        var token = GetVinTokenModel();

        if (token.Ip != HttpContext.GetClientUserIp())
        {
            var clientInfo = VinApp.HttpContext.GetClientDetail(true);
            token.Ip = clientInfo.Ip;
            token.Location = clientInfo.Location;
            token.Browser = clientInfo.Browser;
            token.Os = clientInfo.Os;
            token.UserAgent = clientInfo.UserAgent;
            token.Device = clientInfo.Device;
            await RedisCache.SetCacheAsync(tokenKey, token, TimeSpan.FromMinutes(option.OnlineHeartbeatRenewalMinutes));
        }

        await RedisCache.ExpireAsync(tokenKey, TimeSpan.FromMinutes(option.OnlineHeartbeatRenewalMinutes));
        return await RedisCache.SetCacheAsync(MonitorConstants.OnlineKey.Format(HttpContext.GetUserId()!), DateTime.Now,
            TimeSpan.FromSeconds(option.OnlineHeartbeatSeconds));
    }

    public async Task<bool> ForceLogoutAsync(string sessionId, string? prefix = null)
    {
        VinSecurityTool.LogoutAuthorizeToken(sessionId, prefix);
        return await Task.FromResult(true);
    }

    public async Task<bool> LogoutAsync()
    {
        await RedisCache.RemoveCacheAsync(MonitorConstants.OnlineKey.Format(HttpContext.GetUserId()!));
        return await Task.FromResult(true);
    }
}